/*
 * Copyright (c) 2006-2019, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-07-24     Tanek        the first version
 * 2018-11-12     Ernest Chen  modify copyright
 */

#include <stdint.h>
#include <rthw.h>
#include <rtthread.h>
#include <bsp_usart.h>
#include <bsp_gpio.h>
#include <board.h>

/* RT-Thread board init */
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE (1024 * 8)
static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
    return rt_heap;
}

RT_WEAK void *rt_heap_end_get(void)
{
    return rt_heap + RT_HEAP_SIZE;
}
#endif

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1)
    {
    }
    /* USER CODE END Error_Handler_Debug */
}

// Updates the variable SystemCoreClock and must be called
// whenever the core clock is changed during program execution.
extern void SystemCoreClockUpdate(void);

// Holds the system core clock, which is the system clock
// frequency supplied to the SysTick timer and the processor
// core clock.
extern uint32_t SystemCoreClock;

/* defined in stm32h7xx_hal_msp.c */
extern void SystemClock_Config(void);            /* config system clock, AHB1/2/3/4, APB1/2/3/4... */
extern void cortex_m7_mpu_init(void);            /* MPU initialization */
extern void cortex_m7_L1_cpu_cache_enable(void); /* Cache enable */

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
static void MX_GPIO_Init(void)
{
    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOH_CLK_ENABLE(); /* OSC_IN and OSC_OUT in GPIOH port */
    __HAL_RCC_GPIOE_CLK_ENABLE(); /* UART7 TX/RX GPIOE */
    __HAL_RCC_GPIOD_CLK_ENABLE(); /* GPIOD enable */
}

static struct rt_memheap DTCM_heap;    // memheap 控制块
static struct rt_memheap D2_sram_heap; // memheap 控制块
static struct rt_memheap D3_sram_heap; // memheap 控制块
static int8_t sram_init = -1;
int internal_sram_init(void)
{
    if (sram_init >= 0)
        return sram_init;

    if (rt_memheap_init(&DTCM_heap, "DTCM_sram", (void *)DTCM_START_ADDR, DTCM_SIZE) != RT_EOK)
    {
        sram_init = -1;
    }
    else
    {
        sram_init = 0;
    }

    if (rt_memheap_init(&D2_sram_heap, "DTCM_sram", (void *)D2_RAM_START_ADDR, D2_RAM_SIZE) != RT_EOK)
    {
        sram_init = -1;
    }

    if (rt_memheap_init(&D3_sram_heap, "DTCM_sram", (void *)D3_RAM_START_ADDR, D3_RAM_SIZE) != RT_EOK)
    {
        sram_init = -1;
    }
    return sram_init;
}

void *memheap_alloc(enum sram_region sram, uint32_t size)
{
    void *malloc_ptr = 0;

    switch (sram)
    {
    case (enum sram_region)DTCM:
        malloc_ptr = rt_memheap_alloc(&DTCM_heap, size);
        break;

    case (enum sram_region)D2_SRAM:
        malloc_ptr = rt_memheap_alloc(&D2_sram_heap, size);
        break;

    case (enum sram_region)D3_SRAM:
        malloc_ptr = rt_memheap_alloc(&D3_sram_heap, size);
        break;

    case (enum sram_region)Any:
        malloc_ptr = rt_memheap_alloc(&DTCM_heap, size);
        if (!malloc_ptr)
        {
            malloc_ptr = rt_memheap_alloc(&D2_sram_heap, size);
            if (!malloc_ptr)
            {
                malloc_ptr = rt_memheap_alloc(&D3_sram_heap, size);
            }
        }
        break;

    default:
        break;
    }

    return malloc_ptr;
}

/* SysTick configuration */
void rt_hw_systick_init(void)
{
    HAL_SYSTICK_Config((HAL_RCCEx_GetD1SysClockFreq()) / RT_TICK_PER_SECOND);
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/**
 * This function will initial your board.
 */
void rt_hw_board_init()
{
    /* 系统上电复位后，通过内部高速时钟 HSI 运行（主频 64MHz），Flash 工作在 0 等待周期，所有外
    设除了 SRAM、Flash、JTAG 和 PWR，时钟都是关闭的

    使能了外设时钟后，不能立即操作对应的寄存器，要加延迟。不同外设延迟不同
    如果是 AHB 的外设，使能了时钟后，需要等待 2 个 AHB 时钟周期才可以操作这个外设的寄存器。
    如果是 APB 的外设，使能了时钟后，需要等待 2 个 APB 时钟周期才可以操作这个外设的寄存器。
    当前 HAL 库的解决方案是在使能了外设时钟后，再搞一个读操作，算是当做延迟用。

    */
    cortex_m7_mpu_init();

    cortex_m7_L1_cpu_cache_enable();

    /* STM32H7xx HAL library initialization:
     - Systick timer is configured by default as source of time base, but user
       can eventually implement his proper time base source (a general purpose
       timer for example or other time source), keeping in mind that Time base
       duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and
       handled in milliseconds basis.
     - Set NVIC Group Priority to 4
     - Low Level Initialization
   */
    HAL_Init();
    

    /* enable interrupt */
    __set_PRIMASK(0);
    /* System clock initialization */
    SystemClock_Config();
    /* disbale interrupt */
    __set_PRIMASK(1);

    rt_hw_systick_init();

    MX_GPIO_Init();

    /* user init */
    bsp_gpio_init();
    bsp_debug_uart_init();
    bsp_download_uart_init();

    /* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    HAL_IncTick();
    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

/* re-implement tick interface for STM32 HAL */
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
    /* Return function status */
    return HAL_OK;
}

uint32_t HAL_GetTick(void)
{
    return rt_tick_get() * 1000 / RT_TICK_PER_SECOND;
}

void HAL_SuspendTick(void)
{
}

void HAL_ResumeTick(void)
{
}

void HAL_Delay(__IO uint32_t Delay)
{
}

#define DBG_TAG "board"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

int clock_information(void)
{
    LOG_I("System Clock information");
    LOG_I("SYSCLK_Frequency = %d", HAL_RCC_GetSysClockFreq());
    LOG_I("HCLK_Frequency   = %d", HAL_RCC_GetHCLKFreq());
    LOG_I("PCLK1_Frequency  = %d", HAL_RCC_GetPCLK1Freq());
    LOG_I("PCLK2_Frequency  = %d", HAL_RCC_GetPCLK2Freq());

    return RT_EOK;
}
INIT_BOARD_EXPORT(clock_information);